﻿/*   
    Copyright (C) 2009 Galaktika Corporation ZAO

    This file is a part of Galaktika.BusinessMonitor
 
    Galaktika.BusinessMonitor is a free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
      
    You should have received a copy of the GNU General Public License
    along with Galaktika.BusinessMonitor.  If not, see <http://www.gnu.org/licenses/>.
  
    If GPL v.3 is not suitable for your products or company,
    Galaktika Corp provides Galaktika.BusinessMonitor under a flexible commercial license
    designed to meet your specific usage and distribution requirements.
    If you have already obtained a commercial license from Galaktika Corp,
    you can use this file under those license terms.
*/

using System;
using System.Collections.Generic;
using System.Threading;
using System.Windows;
using Galaktika.BI.Silverlight.ClientApp.Items;

namespace Galaktika.BI.Silverlight.ClientApp.Services
{
    public class UserTaskThreadPool
    {
        #region AsyncWorkItem
        private class AsyncWorkItem
        {
            private AsyncWorkItem()
                : this(null, null)
            {
            }

            public AsyncWorkItem(WaitCallback method, object state)
            {
                this.Method = method;
                this.State = state;
            }

            public AsyncWorkItem(WaitCallback method, object state, string formId)
            {
                this.Method = method;
                this.State = state;
                this.FormId = formId;
            }

            public readonly string FormId = string.Empty;
            public readonly WaitCallback Method;
            public readonly object State;
            public static readonly AsyncWorkItem Empty = new AsyncWorkItem();
        }
        #endregion
        internal static int MaxThreads = 2;
        private const double PLANNER_THREAD_DELAY = 0.1; // 1 sec = 1000 milliseconds
        private static AutoResetEvent m_Planner;

        static UserTaskThreadPool()
        {
            m_Planner = new AutoResetEvent(false);
            m_WorkThread = new AutoResetEvent(false);
            m_ThreadCache = new List<Thread>(MaxThreads);
            m_WorkingThreads = new List<Thread>(MaxThreads);
            m_Queue = new Queue<AsyncWorkItem>();

            Thread plannerThread = new Thread(new ThreadStart(PlannerLoop));
            plannerThread.Name = "Async pool planner";
            plannerThread.IsBackground = true;
            plannerThread.CurrentUICulture = Thread.CurrentThread.CurrentUICulture;
            plannerThread.Start();
        }

        //class InterlockedExpression
        //{
        //    public static bool IsTrue(object lockObj, Func<bool> expression)
        //    {
        //        lock (lockObj)
        //        {
        //            return expression();
        //        }
        //    }
        //}

        /// <summary>
        /// Цикл планировщика работ
        /// </summary>
        static void PlannerLoop()
        {
            do
            {
                m_Planner.WaitOne();
                while (true)
                {
                    lock (m_Queue)
                    {
                        if (m_Queue.Count == 0)
                        {
                            break;
                        }

                        StatusBarManager.SetBarCount(m_Queue.Count);
                    }

                    // Получаем свободный поток
                    // Если все потоки заняты то ждем первого освободившегося
                    if (EnsureIdleThread())
                    {
                        m_WorkThread.Set();
                    }
                    else
                    {
                        Thread.Sleep(TimeSpan.FromSeconds(PLANNER_THREAD_DELAY));
                    }
                }
            } while (true);
        }

        // Кэш потоков
        // Помещенные в него объекты никогда не удаляются
        private static List<Thread> m_ThreadCache;
        // Список свободных потоков
        private static List<Thread> m_WorkingThreads;
        // Блокировщик рабочих потоков
        private static AutoResetEvent m_WorkThread;
        // Очередь работ на выполнение
        private static Queue<AsyncWorkItem> m_Queue;

        /// <summary>
        /// Добавляет работу на выполнение в асинхронном режиме
        /// </summary>
        /// <param name="start">Делегат</param>
        /// <param name="state">Параметр</param>
        public static void AddWork(WaitCallback start, object state, string formId)
        {
            lock (m_Queue)
            {
                // Добавляем работу в очередь
                m_Queue.Enqueue(new AsyncWorkItem(start, state, formId));

                StatusBarManager.SetBarMaximum(m_Queue.Count + 1);
            }
            // Уведомляем планировщика о появлении новой работы
            m_Planner.Set();
        }

        private static bool EnsureIdleThread()
        {
            lock (m_ThreadCache)
            {
                // Ищем свободный поток
                //
                foreach (Thread thread in m_ThreadCache)
                {
                    //if ((thread.ThreadState & ThreadState.WaitSleepJoin) != 0)
                    lock (m_WorkingThreads)
                    {
                        if (!m_WorkingThreads.Contains(thread))
                        {
                            return true;
                        }
                    }
                }

                // Если поток не найден пробуем создать новый
                //
                if (m_ThreadCache.Count < MaxThreads)
                {
                    CreateThread();
                    return true;
                }

                return false;
            }
        }

        /// <summary>
        /// Создает новый рабочий поток
        /// </summary>
        private static void CreateThread()
        {
            Thread thread = new Thread(new ThreadStart(InfiniteLoop));
            thread.Name = string.Format("Async pool worker {0}", m_ThreadCache.Count);
            thread.CurrentUICulture = Thread.CurrentThread.CurrentUICulture;
            thread.IsBackground = true;
            m_ThreadCache.Add(thread);
            thread.Start();
        }

        /// <summary>
        /// Метод рабочего потока
        /// </summary>
        private static void InfiniteLoop()
        {
            do
            {
                // Ждем сигнала планировщика
                m_WorkThread.WaitOne();
                AsyncWorkItem work = null;
                // Вносим текущий поток в список исполняемых потоков
                lock (m_WorkingThreads)
                {
                    m_WorkingThreads.Add(Thread.CurrentThread);
                }

                lock (m_Queue)
                {
                    // Выбираем работу из очереди
                    if (m_Queue.Count > 0)
                    {
                        work = m_Queue.Dequeue();
                    }
                }
                // Если удалось выбрать работу
                // выполняем ее
                if (work != null)
                {
                    work.Method.Invoke(work.State);
                }


                // Работа завершена
                // удаляем себя из списка исполняемых потоков
                int wtCount = 0;
                lock (m_WorkingThreads)
                {
                    m_WorkingThreads.Remove(Thread.CurrentThread);
                    wtCount = m_WorkingThreads.Count;
                }

                lock (m_Queue)
                {
                    if (m_Queue.Count == 0 && wtCount == 0)
                    {
                        StatusBarManager.SetBarEmpty();
                    }
                }
            }
            while (true);
        }

        public static bool IsEmptyCurrentPage(string workId)
        {
            lock (m_Queue)
            {
                foreach (var workItem in m_Queue)
                {
                    if (workItem.FormId.Equals(workId))
                    {
                        return false;
                    }
                }
            }
            return true;
        }

        public static bool IsPooled(Thread thread)
        {
            lock (m_ThreadCache)
            {
                return m_ThreadCache.Contains(thread);
            }
        }
    }
}
